﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Borg.Framework
{
    public class PropertyCommand : ICommand
    {
        private readonly string[] _arguments =
            new[]
                {
                    "Runtime",
                    "ExtensionTypeInfo",
                    "SlotTypeInfo",
                    "ExtensionInfo",
                    "SlotInfo",
                    "Extension"
                };

        private readonly Dictionary<string, CommandHandler>
            _propertyCommandHandler = new Dictionary<string, CommandHandler>();

        public PropertyCommand()
        {
            _propertyCommandHandler.Add("Runtime", RuntimeProperty);
        }

        public IEnumerable<string> GetArguments(string[] command, int pos)
        {
            string[] args = null;
            PropertyInfo[] properties;
            int i;
            
            switch (pos)
            {
                case 0:
                    return _arguments;

                case 1:
                    if (command.Length >= 2)
                    {
                        var cmd = command[1];

                        if (cmd != null)
                        {
                            if (cmd != "ExtensionTypeInfo")
                            {
                                switch (cmd)
                                {
                                    case "Extension":
                                    case "ExtensionInfo":
                                        args = new string[Runtime.Repository.ExtensionInfos.Count];
                                        for (i = 0; i < args.Length; i++)
                                            args[i] = Runtime.Repository.ExtensionInfos[i].Name;
                                        return args;
                                    
                                    case "SlotTypeInfo":
                                        args = new string[Runtime.Repository.SlotTypeInfos.Count];
                                        for (i = 0; i < args.Length; i++)
                                            args[i] = Runtime.Repository.SlotTypeInfos[i].Name;
                                        return args;
                                    
                                    case "SlotInfo":
                                        args = new string[Runtime.Repository.SlotInfos.Count];
                                        for (i = 0; i < args.Length; i++)
                                            args[i] = Runtime.Repository.SlotInfos[i].Name;
                                        return args;
                                    
                                    case "Runtime":
                                        properties = typeof(Runtime).GetProperties();
                                        args = new string[properties.Length];
                                        for (i = 0; i < args.Length; i++)
                                            args[i] = properties[i].Name;
                                        return args;
                                }
                            }
                            else
                            {
                                args = new string[Runtime.Repository.ExtensionTypeInfos.Count];

                                for (i = 0; i < args.Length; i++)
                                    args[i] = Runtime.Repository.ExtensionTypeInfos[i].Name;
                            }
                        }

                        return args;
                    }

                    return new string[0];

                case 2:
                    if (command.Length >= 3 && command[2] != "Runtime")
                    {
                        var objects = GetObjects(command);

                        if (objects.Length == 0)
                            return new string[0];

                        properties = objects[0].GetType().GetProperties();
                        args = new string[properties.Length];

                        for (i = 0; i < args.Length; i++)
                            args[i] = properties[i].Name;

                        return args;
                    }

                    return new string[0];
            }
            return null;
        }

        private static object[] GetObjects(string[] command)
        {
            object[] obs;
            string cmd;

            if (command[1] != "Extension")
                obs = RuntimeCommands.Find(command, 1, out cmd);
            else
            {
                var elements =
                    RuntimeCommands.Find(new[] {"ExtensionInfo", command[2]}, 0, out cmd);

                if (elements.Length == 0)
                    return new object[0];

                obs = new object[elements.Length];

                for (var i = 0; i < obs.Length; i++)
                    obs[i] = ((ExtensionInfo) elements[i]).Object;
            }

            return obs;
        }

        private string Property(string[] command)
        {
            object[] objects;

            if (command.Length < 2)
                return "Too few arguments.";

            if (!RuntimeCommands.ContainsArgument(command[1], _arguments))
                return string.Format("Invalid argument \"{0}\"", command[1]);

            var index = 2;
            var flag = false;
            
            if (command[1] == _arguments[0])
            {
                objects = new object[1];

                if (command.Length < 3)
                    flag = true;
            }
            else
            {
                index++;

                if (command.Length < 3)
                    return "Too few arguments";

                objects = GetObjects(command);
            }

            if (objects.Length == 0)
                return string.Format("{0} {1} not found", command[1], command[2]);

            if (command.Length <= index)
                flag = true;

            var builder = new StringBuilder();
            
            var type = objects[0] != null
                ? objects[0].GetType()
                : typeof(Runtime);
            
            if (flag)
            {
                foreach (PropertyInfo info in type.GetProperties())
                    builder.AppendLine(info.Name);

                return builder.ToString();
            }

            var vals = command[index].Split(new[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
            var property = type.GetProperty(vals[0]);
            
            if (property != null)
            {
                if (vals.Length > 1)
                {
                    if (!property.CanWrite)
                        return string.Format("Property \"{0}\" is readonly.", vals[0]);

                    SetCommand.SetValue(property, objects, vals[1]);
                }

                if (!property.CanRead)
                    return string.Format("Property \"{0}\" is writeonly", vals[0]);

                foreach (var ob in objects.Select(obj => property.GetValue(obj, null)))
                    builder.AppendLine(ob != null ? ob.ToString() : "NULL");

                return builder.ToString();
            }

            return string.Format("Property \"{0}\" not found", vals[0]);
        }

        private string Property1(string[] command)
        {
            if (command.Length > 1)
            {
                CommandHandler handler;

                return _propertyCommandHandler.TryGetValue(command[1], out handler)
                    ? handler(command)
                    : string.Format("'{0}' not found", command[1]);
            }

            return string.Empty;
        }

        private static string RuntimeProperty(string[] command)
        {
            if (command == null || command.Length < 3)
                return "Property parameter missing";

            var parameters = command[2].Split(new[] { '=' }, StringSplitOptions.RemoveEmptyEntries);

            if (parameters.Length == 0)
                return "Property parameter missing";

            var result = false;
            var flag2 = false;

            var format = string.Format("Property {0}={{0}}", parameters[0]);
            
            if (parameters.Length > 1)
            {
                if (!bool.TryParse(parameters[1], out result))
                    return string.Format("Invalid property value: {0}", parameters[1]);

                flag2 = true;
            }

            switch (parameters[0])
            {
                case "AutoOpen":
                    if (flag2)
                        Runtime.AutoOpen = result;

                    return string.Format(format, Runtime.AutoOpen);

                case "AutoRegister":
                    if (flag2)
                        Runtime.AutoRegister = result;

                    return string.Format(format, Runtime.AutoRegister);

                case "AutoPlug":
                    if (flag2)
                        Runtime.AutoPlug = result;

                    return string.Format(format, Runtime.AutoPlug);

                case "AutoRelease":
                    if (flag2)
                        Runtime.AutoRelease = result;

                    return string.Format(format, Runtime.AutoRelease);

                case "LazyLoad":
                    if (flag2)
                        Runtime.LazyLoad = result;

                    return string.Format(format, Runtime.LazyLoad);

                case "":
                    return "Property parameter missing";
            }

            return string.Format("Property {0} not found", parameters[0]);
        }

        public CommandHandler Handler
        {
            get { return Property; }
        }

        public string Help
        {
            get { return "Help for command \"property\""; }
        }

        public string Name
        {
            get { return "property"; }
        }
    }
}

